package rmi.billing.server;

import java.io.IOException;
import java.math.BigInteger;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import org.apache.log4j.*;
import rmi.billing.exceptions.LoginFailedRemoteException;
import rmi.domain.RegistrySingleton;


public class BillingServer implements BillingServerInterface {
public static Logger log = Logger.getLogger(BillingServer.class);

private BillingServerSecure billingServerSecure = new BillingServerSecureImpl();
private BillingServerSecure billingSecureStub;

	/*
	 * Main class of the Billing Server. A BillingServer reference is bind to the registry. 
	 * Also holds the method Login, which returns the BillingServerSecure to the ManagementClient if login was successfull
	 * @param billing.bindingName
	 */
	public static void main(String[] args) {
		
		//check parameter
		String billingref="";
		if(args.length==1){
			billingref=args[0];			
		}
		else{
			//Usage
			System.out.println("Usage: java BillingServer <billing.bindingName>");
			return;
		}
		//gets registry if already exists or creates a new instance
		Registry reg = RegistrySingleton.initialRegistry();
		BillingServer billingServer = new BillingServer();
		
		try {
			//export billingServer to make it available from outside
			BillingServerInterface stub = (BillingServerInterface) UnicastRemoteObject.exportObject(billingServer,0);
			//binding the billingServer to the registry
			reg.bind(billingref, billingServer);			
			
		} catch (RemoteException e) {
			log.error("Error exporting BillingServer reference "+e.getMessage());
		} catch (AlreadyBoundException e) {
			log.error("Object: BillingServer reference is already bound: "+ e.getMessage());	
		}	
		
		//loop to listen for the shutdown command
		while(true){
			Scanner sc = new Scanner(System.in);
			if(sc.nextLine().equals("!exit")){
				System.out.println("Shutting down BillingServer");
				try {
					//unbind billingServer from the registry
					reg.unbind(billingref);
				} catch (AccessException e) {
					log.debug("No Access to Registry");
					
				} catch (RemoteException e) {
					log.debug("No Access to Registry");
				} catch (NotBoundException e) {
					log.debug("No BillingServer reference found in Registry");
				}
				try {
					//unexport is necessary or the BillingServer won't shut down
					UnicastRemoteObject.unexportObject(billingServer, true);
				} catch (NoSuchObjectException e) {
					log.debug("BillingServer was not exportet");
				}
				try{
					//also unexport BillingServerSecure
					billingServer.shutDownSecure();
				} catch (NoSuchObjectException e) {
					log.debug("BillingServerSecure was not exportet");
				}
				break;
			}
			else{
				System.out.println("Error: Unknown Command!");
			}
		}
		
	}
	//shutting down BillingServerSecure in case of exit
	public void shutDownSecure() throws NoSuchObjectException{
		UnicastRemoteObject.unexportObject(billingServerSecure, true);
	}
	//obtaining a reference of the BillingServerSecure
	public BillingServerSecure getBillingServerSecureStub(){
		//billingsecure stub			
		try {						
			billingSecureStub = (BillingServerSecure) UnicastRemoteObject.exportObject(billingServerSecure,0);							
			} catch (RemoteException e) {
				//log.error("Error exporting BillingServerSecure reference "+e.getMessage());
			}
			
		return billingSecureStub;		
	}

	//login mehtod called from the managmentclient, returns the BillingServerSecure if successfull
	@Override 
	public BillingServerSecure login(String username, String password) throws LoginFailedRemoteException,RemoteException{
		String s = this.getValidPassword(username);
		String i = this.getMd5Digest(password);
		log.debug("PW md5 from File="+s+" PW md5="+i);
		
		if (!i.equals(s)) //checks if entered password matches the password in the property file
			throw new LoginFailedRemoteException();
			
		return this.getBillingServerSecureStub(); //reference to a SecureBillingServer remote object
		
		
	}
	
	//read Password from user.properties file
	public String getValidPassword(String username){
		String pw = "";
		java.io.InputStream is = ClassLoader.getSystemResourceAsStream("user.properties");
		if (is != null) {
			java.util.Properties props = new java.util.Properties();
		try {
			props.load(is);
			
			pw = props.getProperty(username);
			
		} catch (IOException e) {
		
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				
				e.printStackTrace();
			}
		}
		} else {
			log.error("Properties file not found!");
		}
		
		return pw;

	}
	
	//get MD5 from entered password
	 public String getMd5Digest(String input)
     {
         try
         {
             MessageDigest md = MessageDigest.getInstance("MD5");
             byte[] messageDigest = md.digest(input.getBytes());
             BigInteger number = new BigInteger(1,messageDigest);
             return number.toString(16);
         }
         catch(NoSuchAlgorithmException e)
         {
             throw new RuntimeException(e);
         }
     }

}
